
#region    [自定义类-VS][20250814][LedTemplate]
using GZ.Modular.Redis;
using ServiceStack.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Input;
using static GZ.Projects.WCS_SVC.bxdualsdk;
using static log4net.Appender.RollingFileAppender;
using GZ.DB.Entity.wcs_hy;
using GZ.DB.Repository.wcs_hy;

namespace GZ.Projects.WCS_SVC
{
    /// <summary>
    /// LED 显示行信息
    /// </summary>
    public class RowInfo
    {
        /// <summary>
        /// 行内容
        /// </summary>
        public string Row { get; set; }

        /// <summary>
        /// 行显示类型
        /// 0随机显示
        /// 1静止显示
        /// 2快速打出
        /// 3向左移动
        /// 4向左连移
        /// 5向上移动
        /// 6向上连移
        /// 7闪烁
        /// </summary>
        public int RowType { get; set; }
    }

    public class LedTemplate
    {
        #region Constants

        private const string GBKEncoding = "GBK"; //编码格式
        private const string GB2312Encoding = "GB2312"; //编码格式
        private const byte AreaType = 0x10; // 区域类型
        private const byte BackgroundFlag = 0x00; // 背景标志
        private const int DefaultTransparency = 101; // 默认透明度
        private const ushort DefaultAreaXHeight = 24; // 默认行高
        private const ushort DefaultAreaYHeight = 52; // 默认行高
        private const ushort DefaultAreaDefYHeight = 18; // 默认行高(标语)
        private const ushort LedWidth = 192; // LED屏宽度
        private const ushort LedHeight = 96; // LED屏高度
        private const ushort Port = 5005; // 控制卡端口
        private const string DefaultFontName = "宋体"; // 默认字体

        #endregion

        /***********************************************************
         * 二、线程安全数据结构
         * 目的：解决多线程并发访问问题
         ***********************************************************/

        #region Thread-safe Collections

        // 更新时间记录（ConcurrentDictionary本身线程安全）
        private static readonly ConcurrentDictionary<string, DateTime> UpdateDictTimer =
            new ConcurrentDictionary<string, DateTime>();

        // 动态区域编号记录
        private static readonly ConcurrentDictionary<string, int> LedDelNumbersMap =
            new ConcurrentDictionary<string, int>();

        #endregion

        /***********************************************************
         * 三、编码器复用
         * 目的：避免重复创建编码器实例，提升性能
         ***********************************************************/

        #region Encoders

        private static readonly Encoding GbkEncoder = Encoding.GetEncoding(GBKEncoding);
        private static readonly Encoding Gb2312Encoder = Encoding.GetEncoding(GB2312Encoding);

        #endregion

        /***********************************************************
         * 四、核心公共方法
         * 目的：对外暴露的LED控制入口
         ***********************************************************/

        #region Public Methods

        /// <summary>
        /// LED显示主控制方法（多线程安全版本）
        /// </summary>
        /// <param name="ip">控制卡IP</param>
        /// <param name="ledName">LED标识名</param>
        public static void LedDispose(string ip, string ledName)
        {
            // 输入参数校验
            if (string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(ledName))
            {
                Conn.HYlog.Error(1, "LED", 1, "IP或LEDName为空");
                return;
            }

            try
            {
                var ledShow = new LedShowRepository().FindList(t =>
                    t.stn_no == ledName &&
                    t.msg_show != null &&
                    t.err_show != null &&
                    t.show_state == 0).FirstOrDefault();

                if (ledShow != null)
                {
                    string msgShow = ledShow.msg_show;
                    string errShow = ledShow.err_show;

                    // 根据模式处理LED显示
                    int errorCode = LedTemplateMode(ip, ledName, msgShow, errShow);

                    // 更新记录
                    if (errorCode == 0)
                    {
                        Conn.HYlog.Info(1, "LED", 1, $"LED内容已显示 - 站台:{ledName}");
                    }
                    else
                    {
                        Conn.HYlog.Error(1, "LED", 1, $"LED显示失败 - 站台:{ledName}, 错误代码:{errorCode}");
                    }
                }
                //else
                //{
                //    Conn.HYlog.Info(1, "LED", 1, $"没有找到待显示的内容 - 站台:{ledName}");
                //}
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"LED显示处理异常 - 站台:{ledName}, 异常:{ex.Message}");
            }
        }

        #endregion

        #region Template Mode Methods

        /// <summary>
        /// LED四区域显示模板方法
        /// 区域布局：
        /// - 左上方：站台名称 (x:0, y:0, width:110, height:24)
        /// - 右上方：系统时间 (x:111, y:0, width:81, height:24)  
        /// - 中间区域：消息显示 (x:0, y:24, width:192, height:52)
        /// - 下方区域：错误显示 (x:0, y:76, width:192, height:18)
        /// </summary>
        /// <param name="ip">控制卡IP地址</param>
        /// <param name="ledName">LED站台名称</param>
        /// <param name="msgShow">要显示的消息内容</param>
        /// <param name="errShow">要显示的错误内容</param>
        /// <returns>0表示成功，其他值表示错误代码</returns>
        private static int LedTemplateMode(string ip, string ledName, string msgShow, string errShow)
        {
            // 输入参数校验
            if (string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(ledName))
            {
                Conn.HYlog.Error(1, "LED", 1, "IP或LEDName参数为空");
                return -1;
            }

            // IP地址编码转换
            byte[] ipBytes;
            try
            {
                ipBytes = GbkEncoder.GetBytes(ip);
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"IP地址编码失败: {ex.Message}");
                return -1;
            }

            List<DynamicAreaParams> dynamicParams = new List<DynamicAreaParams>(4);

            try
            {
                // 清理现有区域
                int delResult = bxdualsdk.bxDual_dynamicArea_DelArea_6G(ipBytes, Port, 0xFF);
                if (delResult != 0)
                {
                    Conn.HYlog.Error(1, "LED", 1, $"清理区域失败，错误代码: {delResult}");
                }

                // 1. 创建左上方区域 - 站台名称
                var stationAreaHeader = CreateAreaHeader(0, 0, 110, 22);
                var stationParam = CreateDynamicParam(
                    stationAreaHeader,
                    new RowInfo { Row = $"站台：{ledName}", RowType = 1 }, // 静止显示
                    0,
                    12 // 字体大小
                );
                dynamicParams.Add(stationParam);

                // 2. 创建右上方区域 - 系统时间
                var timeAreaHeader = CreateAreaHeader(111, 0, 81, 22);
                string currentTime = DateTime.Now.ToString("HH:mm:ss");
                var timeParam = CreateDynamicParam(
                    timeAreaHeader,
                    new RowInfo { Row = currentTime, RowType = 1 }, // 静止显示
                    1,
                    12 // 字体大小
                );
                dynamicParams.Add(timeParam);

                // 3. 创建中间区域 - 消息显示
                if (!string.IsNullOrEmpty(msgShow))
                {
                    var msgAreaHeader = CreateAreaHeader(0, 24, 192, 54);
                    var msgParam = CreateDynamicParam(
                        msgAreaHeader,
                        new RowInfo { Row = msgShow, RowType = 1 }, // 静止显示
                        2,
                        12 // 字体大小
                    );
                    dynamicParams.Add(msgParam);
                }

                // 4. 创建下方区域 - 错误显示
                if (!string.IsNullOrEmpty(errShow))
                {
                    var errAreaHeader = CreateAreaHeader(0, 76, 192, 18);
                    var errParam = CreateDynamicParam(
                        errAreaHeader,
                        new RowInfo { Row = errShow, RowType = 1 }, // 静止显示
                        3,
                        12 // 字体大小
                    );
                    dynamicParams.Add(errParam);
                }

                // 调用SDK发送数据到LED屏
                int result;
                lock (typeof(LedTemplate))
                {
                    result = bxdualsdk.bxDual_dynamicAreaS_AddTxtDetails_6G(
                        ipBytes,
                        Port,
                        bxdualsdk.E_ScreenColor_G56.eSCREEN_COLOR_SINGLE,
                        (byte)dynamicParams.Count,
                        dynamicParams.ToArray()
                    );
                }

                // 更新区域编号记录
                LedDelNumbersMap.AddOrUpdate(ledName, dynamicParams.Count, (k, v) => dynamicParams.Count);

                // 记录操作结果
                if (result == 0)
                {
                   // Conn.HYlog.Info(1, "LED", 1, $"LED显示成功 - 站台:{ledName}, 区域数:{dynamicParams.Count}");

                    // 更新数据库显示状态
                    try
                    {
                        var ledShowRepo = new LedShowRepository();
                        var ledShowEntity = ledShowRepo.FindList(t =>
                            t.stn_no == ledName &&
                            t.msg_show == msgShow &&
                            t.err_show == errShow &&
                            t.show_state == 0).FirstOrDefault();

                        if (ledShowEntity != null)
                        {
                            ledShowEntity.show_state = 1; // 标记为已显示
                            ledShowEntity.update_time = DateTime.Now; // 记录显示时间
                            ledShowRepo.Update(ledShowEntity);
                            //Conn.HYlog.Info(1, "LED", 1, $"数据库显示状态已更新 - 站台:{ledName}");
                        }
                    }
                    catch (Exception dbEx)
                    {
                        Conn.HYlog.Error(1, "LED", 1, $"更新数据库失败: {dbEx.Message}");
                    }
                }
                else
                {
                    Conn.HYlog.Error(1, "LED", 1, $"LED显示失败 - 站台:{ledName}, 错误代码:{result}");
                }

                return result;
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"LED显示异常 - 站台:{ledName}, 异常:{ex.Message}");
                return -1;
            }
            finally
            {
                // 释放非托管资源
                ReleaseUnmanagedResources(dynamicParams);
            }
        }

        #endregion

        /***********************************************************
         * 六、辅助方法
         * 目的：封装可复用逻辑，减少代码重复
         ***********************************************************/

        #region Helper Methods

        /// <summary>
        /// 创建区域头结构（复用创建逻辑）
        /// </summary>
        private static EQareaHeader_G6 CreateAreaHeader(ushort x, ushort y, ushort width, ushort height)
        {
            return new EQareaHeader_G6
            {
                AreaType = AreaType,
                AreaX = x,
                AreaY = y,
                AreaWidth = width,
                AreaHeight = height,
                BackGroundFlag = BackgroundFlag,
                Transparency = DefaultTransparency,
                AreaEqual = 0x00,
                stSoundData = CreateSoundData()
            };
        }

        /// <summary>
        /// 批量创建区域头集合（新增方法）
        /// </summary>
        private static List<EQareaHeader_G6> CreateAreaHeaders(int count, ushort areaHeight)
        {
            var headers = new List<EQareaHeader_G6>();
            for (int i = 0; i < count; i++)
            {
                ushort yPos = (ushort)(i * areaHeight);
                headers.Add(CreateAreaHeader(0, yPos, LedWidth, areaHeight));
            }

            return headers;
        }

        /// <summary>
        /// 构建动态区域参数（封装重复逻辑）
        /// </summary>
        private static DynamicAreaParams CreateDynamicParam(
            EQareaHeader_G6 header,
            RowInfo rowInfo,
            byte areaId,
            ushort fontSize)
        {
            var param = new DynamicAreaParams
            {
                uAreaId = areaId,
                oAreaHeader_G6 = header,
                stPageHeader = CreatePageHeader(fontSize, (byte)rowInfo.RowType)
            };

            // 字体和内容分配
            AssignFontAndContent(ref param, DefaultFontName, rowInfo.Row);
            return param;
        }

        /// <summary>
        /// 分配字体和内容内存（统一内存管理）
        /// </summary>
        private static void AssignFontAndContent(ref DynamicAreaParams param, string font, string content)
        {
            try
            {
                // 确保内容不为空
                if (string.IsNullOrEmpty(content))
                {
                    content = " "; // 空格占位符
                }

                // 字体分配
                byte[] fontBytes = GbkEncoder.GetBytes(font + "\0");
                param.fontName = Marshal.AllocHGlobal(fontBytes.Length);
                Marshal.Copy(fontBytes, 0, param.fontName, fontBytes.Length);

                // 内容分配
                byte[] contentBytes = GbkEncoder.GetBytes(content + "\0");
                param.strAreaTxtContent = Marshal.AllocHGlobal(contentBytes.Length);
                Marshal.Copy(contentBytes, 0, param.strAreaTxtContent, contentBytes.Length);
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"分配字体和内容内存失败：{ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 输入验证方法
        /// </summary>
        private static bool ValidateInput(byte[] ip, List<RowInfo> content, int maxRows)
        {
            // 检查IP地址有效性
            if (ip == null || ip.Length == 0)
            {
                Conn.HYlog.Error(1, "LED", 1, "IP地址无效");
                return false;
            }

            // 内容存在性检查
            if (content == null || content.Count == 0)
            {
                Conn.HYlog.Error(1, "LED", 1, "显示内容为空");
                return false;
            }

            // 行数范围检查（放宽条件，允许少于maxRows）
            if (content.Count > maxRows)
            {
                Conn.HYlog.Error(1, "LED", 1, $"显示内容行数超过最大限制 {maxRows}");
                return false;
            }

            return true;
        }

        #endregion

        /***********************************************************
         * 八、遗留方法（未修改部分）
         * 注意：以下方法需要类似改造，此处保持原样
         ***********************************************************/

        #region Legacy Methods

        /// <summary>
        /// 构造声音数据
        /// </summary>
        /// <returns></returns>
        private static bxdualsdk.EQSound_6G CreateSoundData()
        {
            try
            {
                var stSoundData = new bxdualsdk.EQSound_6G
                {
                    SoundFlag = 0x00,
                    SoundPerson = 0x01,
                    SoundVolum = 6,
                    SoundSpeed = 0x2,
                    SoundDataMode = 0x00,
                    SoundReplayTimes = 0x01,
                    SoundReplayDelay = 200,
                    SoundReservedParaLen = 0x03,
                    Soundnumdeal = 0x00,
                    Soundlanguages = 0x00,
                    Soundwordstyle = 0x00,
                    SoundDataLen = 10,
                    SoundData = BytesToIntptr(Gb2312Encoder.GetBytes("测试语音"))
                };
                return stSoundData;
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"创建声音数据失败: {ex.Message}");
                return new bxdualsdk.EQSound_6G(); // 返回默认值
            }
        }

        /// <summary>
        /// 文本样式
        /// </summary>
        /// <returns></returns>
        private static bxdualsdk.EQpageHeader_G6 CreatePageHeader(ushort fontSize, byte DisplayMode)
        {
            ////DisplayMode
            ///0随机显示
            ///1静止显示
            ///2快速打出
            ///3向左移动
            ///4向左连移
            ///5向上移动
            ///6向上连移
            ///7闪烁

            return new bxdualsdk.EQpageHeader_G6
            {
                PageStyle = 0x00,
                DisplayMode = DisplayMode,
                ClearMode = 0x00,
                Speed = 10,
                StayTime = 100,
                RepeatTime = 1,
                ValidLen = 0,
                CartoonFrameRate = 0x00,
                BackNotValidFlag = 0x00,
                //arrMode = bxdualsdk.E_arrMode.eSINGLELINE,
                arrMode = bxdualsdk.E_arrMode.eMULTILINE,
                fontSize = fontSize,
                color = 0x01, // Font color
                fontBold = 0,
                fontItalic = 0,
                tdirection = bxdualsdk.E_txtDirection.pNORMAL,
                txtSpace = 0,
                Valign = 1,
                Halign = 0
            };
        }

        #region 帮助方法

        /// <summary>
        /// 字节数组转指针（修复内存管理问题）
        /// </summary>
        public static IntPtr BytesToIntptr(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0)
            {
                return IntPtr.Zero;
            }

            try
            {
                IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
                Marshal.Copy(bytes, 0, ptr, bytes.Length);
                return ptr;
            }
            catch (Exception ex)
            {
                Conn.HYlog.Error(1, "LED", 1, $"字节转指针失败: {ex.Message}");
                return IntPtr.Zero;
            }
        }

        #endregion

        #endregion

        /// <summary>
        /// 释放非托管资源（防止内存泄漏）
        /// </summary>
        private static void ReleaseUnmanagedResources(List<DynamicAreaParams> paramsList)
        {
            if (paramsList == null) return;

            lock (typeof(LedTemplate))
            {
                // 使用for循环代替foreach
                for (int i = 0; i < paramsList.Count; i++)
                {
                    var param = paramsList[i];

                    if (param.fontName != IntPtr.Zero)
                    {
                        try
                        {
                            Marshal.FreeHGlobal(param.fontName);
                        }
                        catch (Exception ex)
                        {
                            Conn.HYlog.Error(1, "LED", 1, $"释放字体内存失败: {ex.Message}");
                        }
                        finally
                        {
                            param.fontName = IntPtr.Zero;
                        }
                    }

                    if (param.strAreaTxtContent != IntPtr.Zero)
                    {
                        try
                        {
                            Marshal.FreeHGlobal(param.strAreaTxtContent);
                        }
                        catch (Exception ex)
                        {
                            Conn.HYlog.Error(1, "LED", 1, $"释放内容内存失败: {ex.Message}");
                        }
                        finally
                        {
                            param.strAreaTxtContent = IntPtr.Zero;
                        }
                    }
                }
            }
        }
    }
}

#endregion [自定义类-VS][20250814][LedTemplate]